/*
 * File:   perform.h
 *
 * Author: wijnand.schepens@hogent.be
 *
 * Klassen en functies voor het meten van uitvoeringstijden.
 *
 * Created on 14 maart 2010, 20:35
 *
 *
 * Simpel voorbeeld:
 *
 * void foo() { ... }
 *
 * void test_foo() {
 *     PerformantieMeter pm;
 *     pm.meet(1000, maakMeetFunctie(foo));
 *     pm.report();
 * }
 *
 * void bar(int x) { ... }
 *
 * double test_bar() {
 *     PerformantieMeter pm;
 *     pm.meet(1000, maakMeetFunctie(bar, 10));
 *     return pm_gem_tijd;
 * }
 */

#ifndef _PERFORM_H
#define	_PERFORM_H

#include <iostream>
#include <set>

#include "stopwatch.h"

struct MeetFunctie
{
    virtual void doe_voor() {}
    virtual void doe_tijdens() = 0;
    virtual void doe_na() {}
};

template <typename FUNC>
struct MeetFunctie0 : public MeetFunctie
{
    FUNC &func;
    MeetFunctie0(FUNC &func_) : func(func_) {}
    void doe_tijdens() { func(); }
};

template <typename FUNC>
MeetFunctie0<FUNC> *maakMeetFunctie(FUNC &func) {
    return new MeetFunctie0<FUNC>(func);
}

template <typename FUNC, typename PARAM>
struct MeetFunctie1 : public MeetFunctie
{
    FUNC &func;
    PARAM &param;
    MeetFunctie1(FUNC &func_, PARAM &param_) : func(func_), param(param_) {}
    void doe_tijdens() { func(param); }
};

template <typename FUNC, typename PARAM>
MeetFunctie1<FUNC, PARAM> *maakMeetFunctie(FUNC &func, PARAM &param) {
    return new MeetFunctie1<FUNC, PARAM>(func, param);
}

template <typename FUNC, typename PARAM1, typename PARAM2>
struct MeetFunctie2 : public MeetFunctie
{
    FUNC &func;
    PARAM1 &param1;
    PARAM2 &param2;
    MeetFunctie2(FUNC &func_, PARAM1 &param1_, PARAM2 &param2_) : func(func_), param1(param1_), param2(param2_) {}
    void doe_tijdens() { func(param1, param2); }
};

template <typename FUNC, typename PARAM1, typename PARAM2>
MeetFunctie2<FUNC, PARAM1, PARAM2> *maakMeetFunctie(FUNC &func, PARAM1 &param1, PARAM2 &param2) {
    return new MeetFunctie2<FUNC, PARAM1, PARAM2>(func, param1, param2);
}


class PerformantieMeter
{
public:
    StopWatch stopwatch;
    int ncycles;
    std::multiset<double> tijden;
    double min_tijd;
    double gem_tijd;
    double max_tijd;

    // pre: ncycles > 2
    void meet(int ncycles, MeetFunctie *mf)
    {
        init(ncycles);
        for (int i=0; i<ncycles; i++)
        {
            mf->doe_voor();
            stopwatch.start();
            mf->doe_tijdens();
            stopwatch.stop();
            tijden.insert(stopwatch.tijd_in_sec());
            mf->doe_na();
        }
        exit();
    }

    void init(int ncycles_)
    {
        if (ncycles < 3)
            throw "aantal cycli moet minstens 3 zijn";
        ncycles = ncycles_;
        tijden.clear();
    }


    void exit()
    {
        min_tijd = *(tijden.begin());
        max_tijd = *(tijden.rbegin());
        double som = 0;
        std::set<double>::iterator it=tijden.begin();
        it++;
        for (int i=0; i<ncycles-2; i++) // kleinste en grootste niet meetellen
            som += *it++;
        gem_tijd = som / (ncycles-2);
    }

    void report()
    {
        std::cout << ncycles << " cycli"
                  << "  min: " << min_tijd
                  << "  max: " << max_tijd
                  << "  gem: " << gem_tijd << std::endl;
    }


};



#endif	/* _PERFORM_H */

